package bgpemulation;

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Random;

public class CIDRPrefix implements Comparable<CIDRPrefix>, Serializable{

	private static final long serialVersionUID = 1L;
	public static final int SIZE_OF_PREFIX = 5;
	public static final int MAX_MASK_SIZE = 32;
	public static final int MAX_BYTE_SIZE = 255;
	
	private byte[] bytePrefix;
	private String stringPrefix;
	
	public CIDRPrefix(byte[] bytePrefix){
		this.bytePrefix = bytePrefix;
		this.stringPrefix = toString(bytePrefix);
		normalize();
	}

	public CIDRPrefix(String stringPrefix){
		this.stringPrefix = stringPrefix;
		this.bytePrefix = toBytes(stringPrefix);
		normalize();
	}

	public static byte[] toBytes(String stringPrefix) {
		String[] tokens = stringPrefix.split("[./]");
		byte[] bytePrefix = new byte[SIZE_OF_PREFIX];
		for(int i = 0; i<SIZE_OF_PREFIX; i++){
			bytePrefix[i] = (byte)Integer.parseInt(tokens[i]);
		}
		return bytePrefix;
	}
	
	private void normalize(){
		ByteBuffer b = ByteBuffer.wrap(bytePrefix);
		int ip = b.getInt();
		int maskSize = b.get();
		maskSize = MAX_MASK_SIZE - maskSize;
		if(maskSize > 0){
			int mask = 1;
			while(--maskSize>0){
				mask = mask << 1;
				mask = mask | 1;
			}
			b.clear();
			b.putInt(ip & (~mask));
			b.clear();
			b.get(bytePrefix);
			stringPrefix = toString(bytePrefix);
		}
	}
	
	public static String toString(byte[] bytePrefix) {
		StringBuffer buff = new StringBuffer();
		for(int i=0; i<SIZE_OF_PREFIX; i++){
			if(i==SIZE_OF_PREFIX - 1){
				buff.append("/");
			} else if(i > 0){
				buff.append(".");
			}
			buff.append(mask(bytePrefix[i]));
		}
		return buff.toString();
	}
	
	@Override
	public String toString(){
		return toString(bytePrefix);
	}
	
	private static int mask(byte b) {
		return (int)b & 0xFF;
	}

	public byte[] getBytePrefix() {
		return bytePrefix;
	}

	public String getStringPrefix() {
		return stringPrefix;
	}
	
	public static CIDRPrefix randomPrefix(long seed){
		byte[] bytePrefix = new byte[SIZE_OF_PREFIX];
		Random rng = new Random(seed);
		for(int i = 0; i < SIZE_OF_PREFIX - 1; i++){
			bytePrefix[i] = (byte)rng.nextInt(MAX_BYTE_SIZE + 1);
		}
		bytePrefix[SIZE_OF_PREFIX - 1] = (byte)((byte)rng.nextInt(25) + (byte)8);
		return new CIDRPrefix(bytePrefix);
	}

	public int getPrefixLength() {
		return bytePrefix[bytePrefix.length-1];
	}
	
	public int ipAsInt(){
		return (mask(bytePrefix[0]) << 24) | (mask(bytePrefix[1]) << 16) | (mask(bytePrefix[2]) << 8) | mask(bytePrefix[3]); 
	}

	@Override
	public boolean equals(Object o){
		if(!(o instanceof CIDRPrefix)){
			return false;
		}
		return compareTo((CIDRPrefix)o) == 0;
	}

	@Override
	public int compareTo(CIDRPrefix other) {
		int mask = 0;
		if(getPrefixLength()!=0){
			mask = 0x80000000;
		}
		for(int i = 1; i<Math.min(getPrefixLength(), other.getPrefixLength()); i++){
			mask = mask >> 1;
		}
		if((ipAsInt() & mask)==(other.ipAsInt()&mask)){
			return 0;
		}
		else if((ipAsInt() & mask)>(other.ipAsInt()&mask)){
			return 1;
		} else{
			return -1;
		}
	}
}
